﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net;
using System.IO;
using CommHelper;

namespace SHDServer
{
    /// <summary>
    /// User's http request info
    /// </summary>
    internal class RequestInfo
    {
        private const int BUFFER_SIZE = ConstVar.BUFFER_SIZE;
        public byte[] BufferRead;
        public HttpWebRequest request;
        public HttpWebResponse response;
        public Stream streamResponse;
        public MemoryStream memStreamResponseData;
        public byte[] responseData;
        public ManualResetEvent allDone;
        public StringBuilder asyncResponse;

        public RequestInfo()
        {
            BufferRead = new byte[BUFFER_SIZE];
            request = null;
            response = null;
            streamResponse = null;
            memStreamResponseData = new MemoryStream();
            responseData = null;
            allDone = new ManualResetEvent(false);
            asyncResponse = null;
        }

        /// <summary>
        /// Is get response data
        /// </summary>
        /// <returns></returns>
        public bool IsGetResponse()
        {
            return allDone.WaitOne(0);
        }
    }

    /// <summary>
    /// Http client with HttpWebRequest class
    /// </summary>
    public class HttpWebRequestProgram : IHttpClient
    {
        private const int BUFFER_SIZE = ConstVar.BUFFER_SIZE;        // Buffer size
        private const int DefaultTimeout = ConstVar.DefaultTimeout;  // timeout 15s

        public byte[] Start(string uri, ref StringBuilder asyncResponse, string method)
        {
            try
            {
                RequestInfo ri = new RequestInfo();
                ri.request = (HttpWebRequest)WebRequest.Create(uri);
                ri.request.Method = method;
                IAsyncResult result = (IAsyncResult)ri.request.BeginGetResponse(new AsyncCallback(OnResponse), ri);
                ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), ri.request, DefaultTimeout, true);

                if (null == asyncResponse)      // Use synchronous mode
                {
                    ri.allDone.WaitOne();

                    return ri.responseData;
                }
                else                            // Use asynchronous mode
                {
                    ri.asyncResponse = asyncResponse;
                }

                return null;
            }
            catch (WebException e)
            {
                LogHelper.WriteErrorLog(e.Message);
            }
            catch (Exception e)
            {
                LogHelper.WriteErrorLog(e.Message);
            }
            finally
            {
            }

            return null;
        }

        /// <summary>
        /// Timeout callback
        /// </summary>
        /// <param name="state"></param>
        /// <param name="timeout"></param>
        private void TimeoutCallback(object state, bool timeout)
        {
            if (timeout)
            {
                HttpWebRequest request = state as HttpWebRequest;
                if (null != request)
                {
                    request.Abort();
                }
            }
        }

        /// <summary>
        /// Deal response data
        /// </summary>
        /// <param name="ar"></param>
        private void OnResponse(IAsyncResult ar)
        {
            RequestInfo ri = (RequestInfo)ar.AsyncState;

            try
            {
                ri.response = (HttpWebResponse)ri.request.EndGetResponse(ar);
                ri.streamResponse = ri.response.GetResponseStream();
                ri.streamResponse.BeginRead(ri.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallback), ri);

                return;
            }
            catch (WebException e)
            {
                LogHelper.WriteErrorLog(e.Message);
            }
            catch (Exception e)
            {
                LogHelper.WriteErrorLog(e.Message);
            }
            finally
            {
            }

            ri.allDone.Set();
        }

        /// <summary>
        /// Read response callback
        /// </summary>
        /// <param name="ar"></param>
        private void ReadCallback(IAsyncResult ar)
        {
            RequestInfo ri = (RequestInfo)ar.AsyncState;

            try
            {
                Stream responseStream = ri.streamResponse;
                int read = responseStream.EndRead(ar);

                if (read > 0)
                {
                    ri.memStreamResponseData.Write(ri.BufferRead, 0, read);
                    responseStream.BeginRead(ri.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallback), ri);

                    return;
                }
                else
                {
                    ri.responseData = new byte[ri.memStreamResponseData.Length];
                    Buffer.BlockCopy(ri.memStreamResponseData.ToArray(), 0, ri.responseData, 0, (int)ri.memStreamResponseData.Length);

                    if (null != ri.asyncResponse)
                    {
                        // Save asynchronous response result
                        ri.asyncResponse.Length = 0;
                        ri.asyncResponse.Append(System.Text.Encoding.UTF8.GetString(ri.responseData));
                    }

                    ri.memStreamResponseData.Close();
                    ri.response.Close();
                    ri.streamResponse.Close();
                }
            }
            catch (WebException e)
            {
                LogHelper.WriteErrorLog(e.Message);
            }
            catch (Exception e)
            {
                LogHelper.WriteErrorLog(e.Message);
            }
            finally
            {
            }

            ri.allDone.Set();
        }
    }
}